package com.torry.edss.domain.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.torry.boot.common.util.Preconditions;
import com.torry.edss.application.cache.TradingDayCache;
import com.torry.edss.application.exception.ReplayExceptionEnum;
import com.torry.edss.domain.accessor.CompanyAccessor;
import com.torry.edss.domain.accessor.HisDailyLineAccessor;
import com.torry.edss.domain.accessor.LimitUpAccessor;
import com.torry.edss.domain.model.Company;
import com.torry.edss.domain.model.HisDailyLine;
import com.torry.edss.domain.model.LimitUp;
import com.torry.edss.domain.service.PickingConditionService;
import com.torry.edss.domain.valueobject.MarketPlate;
import com.torry.edss.infras.entity.LimitUpDO;
import com.torry.edss.infras.util.DateUtil;
import com.torry.edss.integration.facade.RemoteApiFacade;
import com.torry.edss.integration.facade.enums.Direction;
import com.torry.edss.integration.facade.result.FetchDailyLineResult;
import com.torry.edss.integration.facade.result.FetchFundFlowInfoResult;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class PickingConditionServiceImpl implements PickingConditionService {
    @Resource
    private CompanyAccessor companyAccessor;
    @Resource
    private RemoteApiFacade remoteApiFacade;
    @Resource
    private LimitUpAccessor limitUpAccessor;
    @Resource
    private HisDailyLineAccessor hisDailyLineAccessor;

    @Override
    public boolean 属于主板且非ST(Company company) {
        return company.getPlate() == MarketPlate.HSZB && !company.getName().contains("ST") && !company.getDelisted();
    }

    @Override
    public boolean 当日放量或平量(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
            return currentKLine.getVolume() >= previousKLine.getVolume();
        } else {
            FetchDailyLineResult kLineData = remoteApiFacade.fetchDayLine(code, date, Direction.CURRENT);
            FetchDailyLineResult previousKLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            return kLineData.getVolume() >= previousKLine.getVolume();
        }
    }

    @Override
    public boolean 当日缩量(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
            return currentKLine.getVolume() < previousKLine.getVolume();
        } else {
            FetchDailyLineResult kLineData = remoteApiFacade.fetchCurrentDayLine(code);
            FetchDailyLineResult previousKLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            return kLineData.getVolume() < previousKLine.getVolume();
        }

    }

    @Override
    public boolean 当日量能小于前一日指定倍数(Long companyId, String date, Double multiple) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {

            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
            return currentKLine.getVolume() < previousKLine.getVolume() * multiple;
        } else {
            FetchDailyLineResult kLineData = remoteApiFacade.fetchCurrentDayLine(code);
            FetchDailyLineResult previousKLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            return kLineData.getVolume() < previousKLine.getVolume() * multiple;
        }
    }

    @Override
    public boolean 次日量能小于当日的指定倍数(Long companyId, String date, Double multiple) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine nextKLineData = hisDailyLineAccessor.getNextDayLine(code, date);
            return nextKLineData.getVolume() < currentKLine.getVolume() * multiple;
        } else {
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchDayLine(code, date, Direction.CURRENT);
            FetchDailyLineResult nextKLineData = remoteApiFacade.fetchNexDayLine(code, date);
            return nextKLineData.getVolume() < currentKLine.getVolume() * multiple;
        }

    }

    @Override
    public boolean 涨停后到指定日期之间没有再次涨停(Long companyId, String limitDate, String endDate) {
        return limitUpAccessor.count(new LambdaQueryWrapper<LimitUpDO>()
                .eq(LimitUpDO::getCompanyId, companyId)
                .gt(LimitUpDO::getDate, limitDate)
                .le(LimitUpDO::getDate, endDate)) == 0;
    }

    @Override
    public boolean 当日低开(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
            return currentKLine.getOpen() < previousKLine.getClose();
        } else {
            FetchDailyLineResult kLineData = remoteApiFacade.fetchCurrentDayLine(code);
            FetchDailyLineResult previousKLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            return kLineData.getOpen() < previousKLine.getPrice();
        }
    }

    @Override
    public boolean 当日跳空低开(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
            return currentKLine.getOpen() < Math.min(previousKLine.getClose(), previousKLine.getOpen());
        } else {
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            FetchDailyLineResult previousKLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            return currentKLine.getOpen() < Math.min(previousKLine.getPrice(), previousKLine.getOpen());
        }
    }

    @Override
    public boolean 当日高开(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
            return currentKLine.getOpen() > previousKLine.getClose();
        } else {
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            FetchDailyLineResult previousKLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            return currentKLine.getOpen() > previousKLine.getPrice();
        }
    }

    @Override
    public boolean 当日跳空高开(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
            return currentKLine.getOpen() > Math.max(previousKLine.getClose(), previousKLine.getOpen());
        } else {

            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            FetchDailyLineResult previousKLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            return currentKLine.getOpen() > Math.max(previousKLine.getPrice(), previousKLine.getOpen());
        }
    }

    @Override
    public boolean 指定日期前一日涨幅小于等于0(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine previousDayLine = hisDailyLineAccessor.getPreviousDayLine(code, date);
            return previousDayLine.getUpPercentage() <= 0;
        } else {
            FetchDailyLineResult result = remoteApiFacade.fetchPreviousDayLine(code, date);
            return result.getUpPercentage() <= 0;
        }
    }

    @Override
    public boolean 涨跌幅大于等于指定值(Long companyId, String date, double zdf) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine kLineDataByDate = hisDailyLineAccessor.getDayLine(code, date);
            return kLineDataByDate.getUpPercentage() >= zdf;
        } else {
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            return currentKLine.getUpPercentage() >= zdf;
        }
    }

    @Override
    public boolean 涨跌幅大于指定值(Long companyId, String date, double zdf) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine kLineDataByDate = hisDailyLineAccessor.getDayLine(code, date);
            return kLineDataByDate.getUpPercentage() > zdf;
        } else {
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            return currentKLine.getUpPercentage() > zdf;
        }
    }

    @Override
    public boolean 跌破涨停板保护位(Long limitUpId, String date) {
        LimitUp limitUp = limitUpAccessor.getById(limitUpId);
        Long companyId = limitUp.getCompanyId();
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine limitUpKLine = hisDailyLineAccessor.getDayLine(code, DateUtil.formatDate(limitUp.getDate()));
            HisDailyLine currentKLine = hisDailyLineAccessor.getDayLine(code, date);
            if (limitUp.getContinuousCount() == 1) {
                return currentKLine.getLow() < limitUpKLine.getLow();
            }
            if (limitUp.getContinuousCount() == 2) {
                HisDailyLine firstLimitData = hisDailyLineAccessor.getPreviousDayLine(code, DateUtil.formatDate(limitUpKLine.getDate()));
                return currentKLine.getLow() < Math.min(firstLimitData.getClose(), limitUpKLine.getLow());
            }
        } else {

            FetchDailyLineResult limitUpKLine = remoteApiFacade.fetchDayLine(code, DateUtil.formatDate(limitUp.getDate()), Direction.CURRENT);
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            if (limitUp.getContinuousCount() == 1) {
                return currentKLine.getLow() < limitUpKLine.getLow();
            }
            if (limitUp.getContinuousCount() == 2) {
                FetchDailyLineResult firstLimitData = remoteApiFacade.fetchPreviousDayLine(code, limitUpKLine.getDate());
                return currentKLine.getLow() < Math.min(firstLimitData.getPrice(), limitUpKLine.getLow());
            }
        }

        throw new RuntimeException();
    }

    @Override
    public boolean 是一字板(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine kLineData = hisDailyLineAccessor.getDayLine(code, date);
            double o = kLineData.getOpen();
            double c = kLineData.getClose();
            double h = kLineData.getHigh();
            double l = kLineData.getLow();
            // 当天一字板
            return o == c && c == h && h == l;
        } else {
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            double o = currentKLine.getOpen();
            double c = currentKLine.getPrice();
            double h = currentKLine.getHigh();
            double l = currentKLine.getLow();
            // 当天一字板
            return o == c && c == h && h == l;
        }
    }

    @Override
    public boolean 当日没有上影线(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine kLineData = hisDailyLineAccessor.getDayLine(code, date);
            double o = kLineData.getOpen();
            double c = kLineData.getClose();
            double h = kLineData.getHigh();

            double topOfBody = Math.max(o, c);
            return h == topOfBody;
        } else {
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            double o = currentKLine.getOpen();
            double c = currentKLine.getPrice();
            double h = currentKLine.getHigh();

            double topOfBody = Math.max(o, c);
            return h == topOfBody;
        }
    }

    @Override
    public boolean 当日没有下影线(Long companyId, String date) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine kLineData = hisDailyLineAccessor.getDayLine(code, date);
            double o = kLineData.getOpen();
            double c = kLineData.getClose();
            double h = kLineData.getHigh();
            double l = kLineData.getLow();
            double bottomOfBody = Math.min(o, c);
            return l == bottomOfBody;
        } else {
            FetchDailyLineResult currentKLine = remoteApiFacade.fetchCurrentDayLine(code);
            double o = currentKLine.getOpen();
            double c = currentKLine.getPrice();
            double l = currentKLine.getLow();
            double bottomOfBody = Math.min(o, c);
            return l == bottomOfBody;
        }
    }

    @Override
    public boolean K线实体部分大于指定值(Long companyId, String date, double percentage) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine kLineData = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);

            double o = kLineData.getOpen();
            double c = kLineData.getClose();
            // 获取前一根 K 线的收盘价
            double previousClose = previousKLine.getClose();

            // 计算当前 K 线实体高度（百分比）
            double entityHeightPercent = Math.abs((c - o) / previousClose) * 100;

            // 判断实体高度是否超过阈值
            return entityHeightPercent > percentage;
        } else {
            FetchDailyLineResult kLineData = remoteApiFacade.fetchCurrentDayLine(code);

            FetchDailyLineResult previousDayLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            double o = kLineData.getOpen();
            double c = kLineData.getPrice();
            double entityHeightPercent = Math.abs((c - o) / previousDayLine.getPrice()) * 100;
            return entityHeightPercent > percentage;
        }
    }

    @Override
    public boolean 上下影线差值大于等于指定值(Long companyId, String date, double percentage) {
        String code = companyAccessor.getCode(companyId);
        if (!isRealTime(date)) {
            HisDailyLine kLineData = hisDailyLineAccessor.getDayLine(code, date);
            HisDailyLine previousKLine = hisDailyLineAccessor.getPreviousDayLine(code, date);

            double o = kLineData.getOpen();
            double c = kLineData.getClose();
            double h = kLineData.getHigh();
            double l = kLineData.getLow();
            // 获取前一根 K 线的收盘价
            double previousClose = previousKLine.getClose();
            // 计算上影线长度（百分比）
            double upperShadowPercent = (h - Math.max(o, c)) / previousClose * 100;
            // 计算下影线长度（百分比）
            double lowerShadowPercent = (Math.min(o, c) - l) / previousClose * 100;

            // 判断上影线和下影线长度是否相差过大
            return Math.abs(upperShadowPercent - lowerShadowPercent) >= percentage;
        } else {

            FetchDailyLineResult kLineData = remoteApiFacade.fetchCurrentDayLine(code);
            FetchDailyLineResult previousDayLine = remoteApiFacade.fetchPreviousDayLine(code, date);
            double o = kLineData.getOpen();
            double c = kLineData.getPrice();
            double h = kLineData.getHigh();
            double l = kLineData.getLow();
            double upperShadowPercent = (h - Math.max(o, c)) / previousDayLine.getPrice() * 100;
            double lowerShadowPercent = (Math.min(o, c) - l) / previousDayLine.getPrice() * 100;
            return Math.abs(upperShadowPercent - lowerShadowPercent) >= percentage;
        }
    }

    @Override
    public boolean 涨停后除首阴外调整期间阴线缩量(Long companyId, String limitDate, String endDate) {
        List<FetchDailyLineResult> allKLineData = remoteApiFacade.fetchAllDailyLines(companyAccessor.getCode(companyId));
        List<String> allDates = allKLineData.stream().map(FetchDailyLineResult::getDate).collect(Collectors.toList());
        int startIndex = allDates.indexOf(limitDate);
        int endIndex = isRealTime(endDate) ? allDates.size() : allDates.indexOf(endDate);
        for (int i = startIndex + 2; i < endIndex; i++) {
            if (allKLineData.get(i).getUpPercentage() < 0 && allKLineData.get(i).getVolume() >= allKLineData.get(i - 1).getVolume()) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean isRealTime(String date) {
        Preconditions.ofTrue(!DateUtil.parseDate(date).isAfter(LocalDate.now()), ReplayExceptionEnum.CURRENT_DATE_IS_NOT_TRADING_DAY, date);
        boolean isToday = DateUtil.formatDate(LocalDate.now()).equals(date);
        if (!isToday) {
            Preconditions.ofTrue(TradingDayCache.isTradingDay(date), ReplayExceptionEnum.CURRENT_DATE_IS_NOT_TRADING_DAY, date);
            return false;
        } else {
            FetchFundFlowInfoResult result = remoteApiFacade.fetchFundFlowInfo(date);
            Preconditions.ofNotNull(result, ReplayExceptionEnum.CURRENT_DATE_IS_NOT_TRADING_DAY, date);
            return true;
        }
    }
}
